

#include "EmbeddedJavaVM.h"

#include "JavaVmInitArg.h"
#include "JvmDllLoader.h"

#include "jni.h"

#include <windows.h>
#include <string.h>
#include <stdio.h>

using namespace Pan;

#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))

// 构造函数
EmbeddedJavaVM::EmbeddedJavaVM(JvmDll &jvm_dll, JavaVmInitArg &arg)
{
    // 主类, 清零
    this->main_class[0] = '\0';

    this->JNI_CreateJavaVM = nullptr; // jni启动函数指针
    this->jni_env = nullptr;          // jni指针
    this->jvm = nullptr;              // jvm指针

    this->init_JNI_CreateJavaVM(jvm_dll);
    this->call_JNI_CreateJavaVM(arg);
}

EmbeddedJavaVM::~EmbeddedJavaVM()
{
    this->call_DestroyJavaVM();
}

bool EmbeddedJavaVM::init_JNI_CreateJavaVM(JvmDll &jvm_dll)
{
    // 已经初始化函数
    if (this->JNI_CreateJavaVM != NULL)
        return true;

    // 没有指定 jvm.dll 句柄
    if (!jvm_dll.is_valid())
        return false;

    // 获取函数地址
    this->JNI_CreateJavaVM =
        (void *)::GetProcAddress //
        (
            static_cast<HMODULE>(jvm_dll.handle()),
            EmbeddedJavaVM::JNI_CreateJavaVM_Name //
        );

    // 返回结果
    return this->JNI_CreateJavaVM != nullptr;
}

bool EmbeddedJavaVM::call_JNI_CreateJavaVM(JavaVmInitArg &init_arg)
{
    // 没有初始化函数地址
    if (this->JNI_CreateJavaVM == nullptr)
        return false;

    typedef jint (*FUNC)(JavaVM **, JNIEnv **, JavaVMInitArgs *);

    FUNC JNI_CreateJavaVM = reinterpret_cast<FUNC>(this->JNI_CreateJavaVM);

    // 调用
    jint ret = JNI_CreateJavaVM //
        (
            reinterpret_cast<JavaVM **>(&this->jvm),
            reinterpret_cast<JNIEnv **>(&this->jni_env),
            reinterpret_cast<JavaVMInitArgs *>(init_arg.args) //
        );

    if (ret < 0 || this->jvm == nullptr || this->jni_env == nullptr)
    {
        // 调用失败
        return false;
    }
    else
    {
        // 调用成功
        return true;
    }
}

bool EmbeddedJavaVM::call_java_main(const char *main_class)
{
    // 空指针检查
    if (main_class == nullptr)
        return false;

    // 检查JNI环境指针
    if (this->jni_env == nullptr)
        return false;

    // 复制 main_class
    for (unsigned long i = 0; i < ARRAY_SIZE(this->main_class); i++)
    {
        this->main_class[i] = main_class[i];
        if (this->main_class[i] == '\0')
        {
            break;
        }

        // 同一使用斜线
        if (this->main_class[i] == '\\' ||
            this->main_class[i] == '.')
        {
            this->main_class[i] = '/';
        }
    }

    // 检查主类是否设置
    if (this->main_class[0] == '\0')
        return false;

    JNIEnv *const jni_env = reinterpret_cast<JNIEnv *>(this->jni_env);

    jclass class_id(nullptr);
    jmethodID method_id(nullptr);

    // 加载启动类

    class_id = jni_env->FindClass(this->main_class);

    if (jni_env->ExceptionCheck() || class_id == nullptr)
    {
        jni_env->ExceptionDescribe();
        jni_env->ExceptionClear();
        return false;
    }

    // 已经找到主类,加载main()函数

    method_id = jni_env->GetStaticMethodID(class_id, "main", "([Ljava/lang/String;)V");

    if (jni_env->ExceptionCheck() || method_id == nullptr)
    {
        // 失败
        jni_env->ExceptionDescribe();
        jni_env->ExceptionClear();
        return false;
    }

    /* 一定要记得: 有什么参数就给什么参数, 一个也不能漏, 否则会发生堆栈错误, 导致虚拟机崩溃 */
    jni_env->CallStaticVoidMethod(class_id, method_id, nullptr /* 恶心死了, 这个nullptr是必要的, 调试了一个小时 */);

    if (jni_env->ExceptionCheck())
    {
        jni_env->ExceptionDescribe();
        jni_env->ExceptionClear();
    }

    return true;
}

bool EmbeddedJavaVM::call_DestroyJavaVM()
{
    if (this->jvm == nullptr)
        return false;

    JavaVM *const jvm(reinterpret_cast<JavaVM *const>(this->jvm));

    // jint ret = jvm->DestroyJavaVM();
    jvm->DestroyJavaVM();

    // return ret != 0;
    return true;
}
